package com.hucheng.rest.validator;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.hucheng.common.constant.Constants;
import com.hucheng.common.constant.RedisConstant;
import com.hucheng.common.constant.UserConstant;
import com.hucheng.common.exception.EduException;
import com.hucheng.common.request.ServiceCoreQuery;
import com.hucheng.common.utils.*;
import com.hucheng.user.vo.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @Author: 凉白开不加冰
 * @Version: 0.0.1V
 * @Date: 2019-06-26
 * @Description: API请求校验
 **/
@Slf4j
public class HttpApiCheck {

    private static final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2HL0Wr+F2JkD2TZOoB0kJW9onz0HL52zjhIZxXKO71b72XEtMRujAxu0jZneHfKdST55t3wZ9xLgx21kTqMB9q/RtCq4x5HT/0cTCCae/i4wwi484hsG/SYynLAdZbV4GEiqQ0HZU/aPPenLe7NY0+5N2Bg8KqseKFdWPzaccxQIDAQAB";

    private static final String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALYcvRav4XYmQPZNk6gHSQlb2ifPQcvnbOOEhnFco7vVvvZcS0xG6MDG7SNmd4d8p1JPnm3fBn3EuDHbWROowH2r9G0KrjHkdP/RxMIJp7+LjDCLjziGwb9JjKcsB1ltXgYSKpDQdlT9o896ct7s1jT7k3YGDwqqx4oV1Y/NpxzFAgMBAAECgYArt+h46s4wY+ySdHsvClixQ3iyt+GuxqbwU59G1ZuE+0I1uGBnNlqn9Nl29yFYyEcOzohVDgZr6O7jISpYOtnCqauns0vUQBWGNMkG0CwrdVPAX2yK/eUkXJd7cIms2pgUXj0P0Tag0R1ajYwBeq9KyY0WJHiPtMNB3B0hZMMMXQJBAOk+88mAqOVJsTy/3rrOWHwpKSVRZmgQFO35eiEY3TwWFeiPp3t8oF1s9hLms4hXns4nrXHnDvCBu5PR6Ddj6LMCQQDH4MhNcCApIxn0ds+66KOEXAVQUzozpkmYYlg1857c3YoJhuWFGW8/itT/G5YbNhKwklXz98Bpoa6PkZERpHCnAkEAohdFg/sNZIpZyUXtmxRbGa3hEjYHXLoQUXDWFroYxPD0OLHcSkzJhSN7vpbknROHIO0h884G68y8dnWhUxzzUQJANz9/vj/m89f6/aK1qqFRSW2yHlDS/qHRJ+jPHyKW38/wrs91s8OjB3pVg6pZZG4e3mWkkooBynDhcle3NJwycwJANiw3Q9MWutTxIu+FODn0WKHilb2GkCXnGqEnVYfrqEuCZ4fh0TUvvzCjYs+5hvkfP1vIzPxQaRxNTIUdoX/HkQ==";

    private static final Properties properties = ReadPropertiesUtils.getProperties("api.properties");

    private ServiceCoreQuery serviceCoreQuery;
    private HttpServletRequest request;
    private StringRedisTemplate stringRedisTemplate;

    public static HttpApiCheck getHttpApiCheck(ServiceCoreQuery serviceCoreQuery, HttpServletRequest request,
                                               StringRedisTemplate stringRedisTemplate) {
        HttpApiCheck httpApiCheck = new HttpApiCheck();
        httpApiCheck.serviceCoreQuery = serviceCoreQuery;
        httpApiCheck.request = request;
        httpApiCheck.stringRedisTemplate = stringRedisTemplate;
        return httpApiCheck;
    }

    public void checkRequest() throws EduException {
        // 数据验签
        sign();
        // 校验action字段
        checkAction();
        // 校验格式
        checkFormat();
        // 校验是否HTTP请求
        checkMethod();
        // 校验accessId
        checkAccessId();
        // 校验用户是否登录
        checkLogin();
    }

    private void sign() throws EduException {
        String charsetName = "utf-8";

        if (ValidatorUtils.isEmpty(serviceCoreQuery.getSign())) {
            throw new EduException(110008);
        }

        Map<String, Object> maps = new LinkedHashMap<>();
        maps.put("Content-Type", request.getHeader("Content-Type"));
        maps.put("action", serviceCoreQuery.getAction());
        maps.put("accessId", serviceCoreQuery.getAccessId());
        maps.put("format", serviceCoreQuery.getFormat());
        maps.put("userId", serviceCoreQuery.getUserId());
        maps.put("reqObject", serviceCoreQuery.getReqObject());

        try {
            String params = RSAUtils.getParams(maps);
            // 客户端数据解密
            byte[] decryptByPrivateKey = RSAUtils.decryptByPrivateKey(Base64.decodeBase64(serviceCoreQuery.getSign()),
                    privateKey);
            String string = new String(decryptByPrivateKey, "utf-8");

            if (!string.equals(params)) {
                throw new EduException(110009);
            }

            String sign = RSAUtils.sign(serviceCoreQuery.getSign(), privateKey, charsetName);
            boolean verify = RSAUtils.verify(serviceCoreQuery.getSign(), sign, publicKey, charsetName);
            if (!verify) {
                throw new EduException(110009);
            }
        } catch (Exception e) {
            throw new EduException(110009);
        }
    }

    private void checkLogin() throws EduException {
        // 校验白名单不需要登录调用
        if (properties.getProperty(serviceCoreQuery.getAction()) == null) {
            throw new EduException(110004);
        }

        if (properties.getProperty(serviceCoreQuery.getAction()).equals(UserConstant.IS_LOGIN)) {
            // 校验是否带上默认accessId
            if (!serviceCoreQuery.getAction().equals("user_defaultAccessId")) {
                if (!serviceCoreQuery.getAccessId().equals(RedisUtils.get(stringRedisTemplate, UserConstant.ACCESS_KEY))) {
                    throw new EduException(100006);
                }
            }
            return;
        }

        if (serviceCoreQuery.getUserId() == null) {
            throw new EduException(100007);
        }

        if (serviceCoreQuery.getUserId() == 0) {
            throw new EduException(100007);
        }

        UserVO userVO = RedisUtils.get(stringRedisTemplate, RedisConstant.HSET_USER_SESSION_KEY +
                DesUtils.encrypt(serviceCoreQuery.getUserId() + ""), UserVO.class);
        if (userVO == null) {
            throw new EduException(100002);
        }

        if (!userVO.getUserId().equals(serviceCoreQuery.getUserId())) {
            throw new EduException(100008);
        }

        // 更新会话时间
        RedisUtils.set(stringRedisTemplate, RedisConstant.HSET_USER_SESSION_KEY +
                        DesUtils.encrypt(serviceCoreQuery.getUserId() + ""),
                JSONObject.toJSONString(userVO), UserConstant.TOKEN_EXPIRE);
    }

    private void checkAccessId() throws EduException {
        String accessId = serviceCoreQuery.getAccessId();

        if (serviceCoreQuery.getAction().equals("user_defaultAccessId")) {
            return;
        }

        if (accessId.isEmpty()) {
            throw new EduException(100005);
        }

        UserVO userVO = RedisUtils.get(stringRedisTemplate, RedisConstant.HSET_USER_SESSION_KEY +
                DesUtils.encrypt(serviceCoreQuery.getUserId() + ""), UserVO.class);
        if (userVO == null) {
            if (!serviceCoreQuery.getAccessId().equals(RedisUtils.get(stringRedisTemplate, UserConstant.ACCESS_KEY))) {
                throw new EduException(100006);
            }
        } else {
            if (!userVO.getAccessId().equals(accessId)) {
                throw new EduException(100006);
            }
        }
    }

    private void checkMethod() throws EduException {
        String requestMethod = StringUtils.getInstance().nullToStrTrim(request.getMethod());
        if (!requestMethod.equalsIgnoreCase(Constants.METHOD_POST)) {
            throw new EduException(110006);
        }
    }

    private void checkFormat() throws EduException {
        String json = serviceCoreQuery.getFormat().toLowerCase();
        if (json.isEmpty()) {
            throw new EduException(110007);
        }

        if (!json.equals("json")) {
            throw new EduException(110007);
        }
    }

    private void checkAction() throws EduException {
        if (serviceCoreQuery.getAction().isEmpty()) {
            throw new EduException(110005);
        }

        if (serviceCoreQuery.getAction().indexOf("_") == -1) {
            throw new EduException(110004);
        }

        if (serviceCoreQuery.getAction().length() > 60) {
            throw new EduException(110004);
        }
    }
}
